import collections, os, re, sqlite3

from forker import fork_and_run

def debug(func):
    def do_it(*args, **kwds):
        print func.__name__, args, kwds
        res = func(*args, **kwds)
        print res
        return res
    return do_it

class DictAsObject(dict):
    def __init__(self, attrs):
        super(DictAsObject, self).__init__(attrs)
        self.__dict__.update(attrs)

class Last:
    def __init__(self):
        self.value = None
    def step(self, *args):
        self.value = args[-1]
    def finalize(self):
        return self.value

def sub_packages(path, base=1, star=False):
    packages = path.split('.')
    for i in range(base, len(packages)):
        yield '.'.join(packages[:i] + (['*'] if star else []))
    yield path

class Summarize:
    def __init__(self):
        self.all = collections.defaultdict(int)
    def step(self, *args):
        for path in args:
            if not path:
                continue
            for package in sub_packages(path, star=True):
                self.all[package] += 1
    def finalize(self, limit=5):
        for base in range(10, 0, -1):
            summary = []
            for count, path in reversed(sorted([(v,k) for k,v in self.all.iteritems()])):
                if path.endswith('*') and path.count('.') < base:
                    continue
                for package in sub_packages(path, base=base, star=True):
                    if package in summary:
                        break
                else:
                    if len(summary) > limit:
                        break
                    summary.append(path)
            if len(summary) <= limit:
                break
        if len(summary) > limit:
            summary[limit:] = []
            summary.append("...")
        return ", ".join(summary)

def dict_factory(cursor, row):
    d = {}
    for idx, col in enumerate(cursor.description):
        d[col[0]] = row[idx]
    return d

def open_db(path):
    conn = sqlite3.connect(path)
    conn.create_aggregate('last', -1, Last)
    conn.create_aggregate('summarize', -1, Summarize)
    #conn.row_factory = sqlite3.Row
    conn.row_factory = dict_factory
    return conn

def get_basename(path):
    # TODO: dedup
    if not os.path.exists(path):
        return path
    path = os.path.abspath(path)
    root = os.path.dirname(path)
    while os.path.exists(os.path.join(root, '__init__.py')):
        root = os.path.dirname(root)
    fully_qualified_path = os.path.splitext(path[len(root) + 1:])[0]
    return fully_qualified_path.replace('/', '.')
    
    

version_regex = re.compile(r"\d+(\.\d+)*((alpha|rc)\d)?$")
basename_regex = re.compile(r"[a-zA-Z]+(\.[a-zA-Z0-9]+)*$")
filename_regex = re.compile(r"\S+(/\S+)+$")
def parse_clause(clause, conn, time_column='time'):
    if clause == '':
        return "1"
    if ';' in clause:
        raise ValueError
    if "'" in clause or '"' in clause:
        # Assume it's a full SQL clause.
        pass
    else:
        conditions = []
        for condition in clause.split():
            if condition == 'all':
                pass
            elif condition == 'last':
                c = conn.cursor()
                last = c.execute("select run_id from runs order by time desc limit 1").fetchone()['run_id']
                print "last", last
                conditions.append("run_id='%s'" % last)
            elif condition == 'penultimate':
                c = conn.cursor()
                penultimate = list(c.execute("select run_id from runs order by time desc limit 2"))[-1]['run_id']
                conditions.append("run_id='%s'" % penultimate)
            elif version_regex.match(condition):
                conditions.append("runs.version='%s'" % condition)
            elif basename_regex.match(condition):
                conditions.append("substr(file, 1, %s)='%s'" % (len(condition)+1, condition))
            elif filename_regex.match(condition):
                conditions.append("path='%s'" % condition)
            elif condition[0] in '<=>':
                op = condition[0]
                date_string = condition[1:]
                if date_string[0] == '=':
                    date_string = date_string[1:]
                    op += '='
                conditions.append("substr(%s, 1, %s) %s '%s'" % (time_column, len(date_string), op, date_string))
            elif ':' in condition:
                ix = condition.index(':')
                conditions.append(condition[:ix] + "='" + condition[1+ix:] + "'");
            else:
                conditions.append("run_id='%s'" % condition)
        clause = " AND ".join(conditions)
        if not clause:
            clause = "1"
    c = conn.cursor()
    sql = "select count(*) from timings left join runs using (run_id) where %s and type='package'" % clause
    count = c.execute(sql).fetchone()['count(*)']
    if not count:
        raise ValueError, "No matching records for %s" % clause
    return clause
